﻿using lhservice.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace lhservice.common.websocket
{
  /// <summary>
  /// websocket中间件
  /// </summary>
  public class WebSocketManagerMiddleware
  {
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;
    public WebSocketManagerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
    {
      _next = next;
      _logger = loggerFactory.CreateLogger<WebSocketManagerMiddleware>();
    }

    public async Task Invoke(HttpContext context)
    {
      /*=========================================================================
        仅处理路径包含“msgHub”的WebSocket请求      
        IsWebSocketRequest判断请求是否为webSocket请求
      ==========================================================================*/
      if (context.Request.Path != "/msgHub" || !context.WebSockets.IsWebSocketRequest)
      {
        await _next.Invoke(context);
        return;
      }

      // 获取 websocket 对象
      var socket = await context.WebSockets.AcceptWebSocketAsync();
      string clientId = Guid.NewGuid().ToString();
      var wsClient = new WebsocketClient
      {
        Id = clientId,
        CreateTime = PublicHelper.GetDtNow(),
        WebSocket = socket
      };

      try
      {
        // 创建消息监听
        await Handle(wsClient);
      }
      catch (Exception ex)
      {
        _logger.LogError(ex, "Echo websocket client {0} err .", clientId);
        await context.Response.WriteAsync("closed");
      }
    }

    /* =============================================================================
      创建一个线程，监听客户端消息
      1、监听客户端发来的消息
      2、收到消息，使用WebSocketMsg模型格式数据
      3、然后交给消息路由处理
    =============================================================================*/
    private async Task Handle(WebsocketClient wsClient)
    {
      _logger.LogInformation($"客户端[{wsClient.Id}]连接，监听中...");
      WebsocketClientCollection.Add(wsClient);
      WebSocketReceiveResult result = null;
      do
      {
        var buffer = new byte[1024 * 20];
        result = await wsClient.WebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        if (result.MessageType == WebSocketMessageType.Text && !result.CloseStatus.HasValue)
        {
          var msgRef = Encoding.UTF8.GetString(buffer);
          var message = JsonConvert.DeserializeObject<MsgFromClient>(msgRef);
          _logger.LogInformation($"接收到[{wsClient.Id}]的消息：\n{JsonConvert.SerializeObject(message)}");
          MessageRoute(wsClient, message);
        }
      } while (!result.CloseStatus.HasValue);
      WebsocketClientCollection.Remove(wsClient);
      //WebsocketClientCollection.UpdateClinetListToMon();
      _logger.LogInformation($"{wsClient.Id}已经断开");
    }

    /*============================================================================
      消息路由（通过action：事务类型 ,区分执行不同的消息处理逻辑)                    
    ============================================================================*/
    private async void MessageRoute(WebsocketClient client, MsgFromClient message)
    {
      switch (message.action)
      {
        // 客户端连接
        case "login":       
          var uid = message.userid;                 
          client.UserId = uid;
          client.Station = message.Station;
          client.StationId = message.StationId;
          client.UserName = message.username;
          //WebsocketClientCollection.UpdateClinetListToMon();
          
          if (uid == "9999") break; // 监控客户端
          var repeatLoginClients = WebsocketClientCollection.GetRepeatLoginClients(uid, client.Id);
          if (repeatLoginClients.Count > 0) {
            WebsocketClientCollection.SendMessageToClientList(repeatLoginClients, "clientclose", JsonConvert.SerializeObject(client));
          }
          
          var res = new Dictionary<string, dynamic>
          {
            ["clientid"] = client.Id,
            ["sessionlistnojoin"] = MpServiceHelper.GetNoJoinSessionList(),
            ["activeSessionList"] = MpServiceHelper.GetActiveSessionList(client.UserId)         
          };       
          await client.SendMessageAsync("logined", JsonConvert.SerializeObject(res));         
          _logger.LogInformation($"{uid}-{client.UserName } 上线");
          break;

        // 客服接入新会话 
        case "join":
          var sid = message.session_id;
          var resOfJoinSession = MpServiceHelper.JoinSession(sid, client.UserId);
          if (resOfJoinSession.ErrCode == "0")
          {
            if (client.SessionList == null) client.SessionList = new List<string>();
            client.SessionList.Add(sid);
          
            await WebsocketClientCollection.SendMessageToAll("joined", JsonConvert.SerializeObject(resOfJoinSession));
            await client.SendMessageAsync("joined", JsonConvert.SerializeObject(resOfJoinSession));

            //WebsocketClientCollection.UpdateClinetListToMon();
          }
          else
          {
            await client.SendMessageAsync("joindenied", JsonConvert.SerializeObject(resOfJoinSession));
          }
          break;

        
        case "modifycustomer":
          MpServiceHelper.ModifyCustomer(message.session_id, message.msgtext);
          var _modifyRes = new Dictionary<string, string>()
          {
            ["session_id"] = message.session_id,
            ["customer"] = message.msgtext
          };
          await client.SendMessageAsync("modifycustomerSucess", JsonConvert.SerializeObject(_modifyRes));
          break;


        // 客服关闭一个会话 
        case "closeSession":
          MpServiceHelper.CloseSession(message.session_id);
          var beClosed = new Dictionary<string, string>()
          {
            ["session_id"] = message.session_id
          };
          await client.SendMessageAsync("sessionClosed", JsonConvert.SerializeObject(beClosed));
          break;

        case "sendmsgtocustomer":
          var msgToCustomer = message.msg;
          msgToCustomer.touser = MpServiceHelper.GetOpenidBySession(message.session_id);

          // 发送消息Mp服务器返回errcode
          int intRes;

          // 通知客服端 发送状态
          var sendmsgsucess = new MsgSendState
          {
            session_id = message.session_id,
            callBackId = message.callBackId,
            state = "sucess"
          };
          intRes = MpServiceHelper.SendMsgToCustomer(msgToCustomer);
          if (intRes != 0)
          {
            sendmsgsucess.state = "err";
            sendmsgsucess.statedetail = MpServiceHelper.GetErrText(intRes);
          }
          else
          {
            var saveMsg = new MsgOfSave()
            {
              SessionId = message.session_id,
              CustomerId = MpServiceHelper.GetCustomerId(message.session_id),
              Dir = "s2c",
              State = 1,
              ToUserName = msgToCustomer.touser,
              MsgType = "text",
              Content = msgToCustomer.text.content,
              CreateTime = (DateTime.Now.Ticks - 621356256000000000) / 10000000
            };
            MpServiceHelper.SaveMsg(saveMsg);
          }

          await client.SendMessageAsync("sendmsgstate", JsonConvert.SerializeObject(sendmsgsucess));
          break;

        // 客户端断开连接
        case "leave":
          await client.SendMessageAsync("clientclose", "{}");
          _logger.LogInformation($"{client.UserId} 断开了连接!");
          WebsocketClientCollection.Remove(client);
          break;

        // 客户端心跳测试回复
        case "alive":
          await client.SendMessageAsync("alive", "{}");
          _logger.LogInformation($"[{client.UserName}-{client.UserId}] is alive");
          break;

        default:
          await client.SendMessageAsync("warn", "无效指令");
          _logger.LogInformation($"undefine action");
          break;
      }
    }
  }
}
